using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace repragator
{
	public class GCodeParser
	{
		public event DebugMessageHandler DebugMessageEvent;
		
		public List<string> RawGCode
		{
			get;
			set;
		}
		
		public List<Layer> RenderableGCode
		{
			get;
			set;
		}
		
		public GCodeParser ()
		{
			this.RawGCode = new List<string>();
			
			this.RenderableGCode = new List<Layer>();
		}
		
		public void Parse(string filename)
		{
			string line, temp;
			int discardedLines = 0;
			Layer currentLayer = null;
			double lastExtruded = 0, totalExtruded = 0;
			
			this.RawGCode.Clear();
			
			this.RenderableGCode.Clear();
			
			this.DebugMessage(string.Format("Parsing '{0}'", filename));
			
			using (StreamReader reader = new StreamReader(filename))
			{
				while (!reader.EndOfStream)
				{
					double xcode = -1, ycode = -1, zcode = -1, ecode = -1;
					
					line = reader.ReadLine();
					
					if (line == string.Empty || line.StartsWith(";"))
					{
						discardedLines++;
						
						continue;	
					}
					
					if (line.Contains(";"))
					{
						line = line.Substring(0, line.IndexOf(";") - 1);	
					}
					
					this.RawGCode.Add(line);
					
					if (!line.StartsWith("G"))
						continue;
					
					if (Regex.IsMatch(line, "X[0-9]+(?:\\.[0-9]*)?"))
					{
						temp = Regex.Match(line, "X[0-9]+(?:\\.[0-9]*)?").Value;
						
						temp = temp.Substring(1);
						
						xcode = double.Parse(temp);
					}
					
					if (Regex.IsMatch(line, "Y[0-9]+(?:\\.[0-9]*)?"))
					{
						temp = Regex.Match(line, "Y[0-9]+(?:\\.[0-9]*)?").Value;
						
						temp = temp.Substring(1);
						
						ycode = double.Parse(temp);
					}
					
					if (Regex.IsMatch(line, "Z[0-9]+(?:\\.[0-9]*)?"))
					{
						temp = Regex.Match(line, "Z[0-9]+(?:\\.[0-9]*)?").Value;
						
						temp = temp.Substring(1);
						
						zcode = double.Parse(temp);
						
						if (currentLayer != null)
						{
							this.RenderableGCode.Add(currentLayer);
						}
						
						currentLayer = new Layer();
					}
					
					if (Regex.IsMatch(line, "E[0-9]+(?:\\.[0-9]*)?"))
					{
						temp = Regex.Match(line, "E[0-9]+(?:\\.[0-9]*)?").Value;
						
						temp = temp.Substring(1);
						
						ecode = double.Parse(temp);
					
						if (line.StartsWith("G92") && ecode == 0)
						{
							totalExtruded += lastExtruded;
						}
						
						lastExtruded = ecode;
					}
					
					if (xcode >= 0 || ycode >= 0)
					{
						currentLayer.Moves.Add(new Move(xcode, ycode, (ecode >= 0) ? true : false));
					}
				}
				
				this.RenderableGCode.Add(currentLayer);
			}
			
			this.RawGCode.Reverse();
			
			this.DebugMessage(string.Format("Processed {0} lines", this.RawGCode.Count));
			
			this.DebugMessage(string.Format("Discarded {0} lines (either blank or comments)", discardedLines));
			
			this.DebugMessage(string.Format("Object has {0} layers", this.RenderableGCode.Count));
			
			this.DebugMessage(string.Format("Object will use {0}mm of filament", totalExtruded));
		}
		
		public void DebugMessage(string message)
		{
			if (this.DebugMessageEvent != null)
				this.DebugMessageEvent(message);	
		}
	}
}